package blms.util.expression;

import java.io.IOException;
import java.text.ParseException;
import java.util.Set;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;

import blms.domain.Expression;
import blms.exception.SyntaxErrorException;
import blms.util.BLMSMessages;

public class AntlrExpression implements Expression {

	private static final long serialVersionUID = 6949899826934657383L;

	private final CommonTree tree;
	private final String expression;

	public AntlrExpression(final String expression, final Set<String> variables)
			throws ParseException {
		this.expression = expression;
		tree = parse(expression, variables);
	}

	private CommonTree parse(final String expr, final Set<String> variables)
			throws ParseException {
		try {
			final ANTLRInputStream input = new ANTLRInputStream(
					new StringStream(expr + "\n"));
			final ExpressionLexer lexer = new ExpressionLexer(input);
			final CommonTokenStream tokens = new CommonTokenStream(lexer);
			final ExpressionParser parser = new ExpressionParser(tokens,
					variables) {
				@Override
				public void emitErrorMessage(String s) {
					throw new SyntaxErrorException();
				}
			};
			return (CommonTree) parser.prog().getTree();
		} catch (final IOException e) {
			throw new ParseException(e.getMessage(), 0);
		} catch (final RecognitionException e) {
			throw new ParseException(e.getMessage(), e.line);
		}
	}
	
	public int evaluate(final Context context) {
		try {
			final CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
			final Evaluator evaluator = new Evaluator(nodes, context);
			return evaluator.eval();

		} catch (final ArithmeticException e) {
			throw new ArithmeticException();

		} catch (final RecognitionException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public String toString() {
		return expression;
	}

}